'use strict';
const sdk = require('indy-sdk');
const indy = require('../../index.js');
const utils = require('../utils');

const MESSAGE_TYPES = {
  OFFER: "urn:sovrin:agent:message_type:sovrin.org/credential_offer",
  REQUEST: "urn:sovrin:agent:message_type:sovrin.org/credential_request",
  CREDENTIAL: "urn:sovrin:agent:message_type:sovrin.org/credential"
};
exports.MESSAGE_TYPES = MESSAGE_TYPES;

exports.handlers = require('./handlers');

exports.getAll = async function () {
  return await sdk.proverGetCredentials(await indy.wallet.get(), {});
};

exports.sendOffer = async function (theirDid, credentialDefinitionId, credentialData) {
  if (theirDid === '_self_') {
    return issueCredentialToSelf(credentialDefinitionId, credentialData);
  }
  else {
    let credOffer = await sdk.issuerCreateCredentialOffer(await indy.wallet.get(), credentialDefinitionId);
    try {
      credOffer.data = JSON.parse(credentialData);
    } catch (e) {
      credOffer.data = {};
      console.log(e);
    }
    await indy.store.pendingCredentialOffers.write(credOffer);
    let pairwise = await sdk.getPairwise(await indy.wallet.get(), theirDid);
    let myDid = pairwise.my_did;
    let message = await indy.crypto.buildAuthcryptedMessage(myDid, theirDid, MESSAGE_TYPES.OFFER, credOffer);
    let meta = JSON.parse(pairwise.metadata);
    let theirEndpointDid = meta.theirEndpointDid;
    return indy.crypto.sendAnonCryptedMessage(theirEndpointDid, message);
  }
};

exports.sendRequest = async function (theirDid, encryptedMessage) {
  let myDid = await indy.pairwise.getMyDid(theirDid);
  let credentialOffer = await indy.crypto.authDecrypt(myDid, encryptedMessage);
  let [, credentialDefinition] = await indy.issuer.getCredDef(await indy.pool.get(), await indy.did.getEndpointDid(), credentialOffer.cred_def_id); // FIXME: Was passing in myDid. Why?
  let masterSecretId = await indy.did.getEndpointDidAttribute('master_secret_id');
  let [credRequestJson, credRequestMetadataJson] = await sdk.proverCreateCredentialReq(await indy.wallet.get(), myDid, credentialOffer, credentialDefinition, masterSecretId);
  indy.store.pendingCredentialRequests.write(credRequestJson, credRequestMetadataJson);
  let message = await indy.crypto.buildAuthcryptedMessage(myDid, theirDid, MESSAGE_TYPES.REQUEST, credRequestJson);
  let theirEndpointDid = await indy.did.getTheirEndpointDid(theirDid);
  return indy.crypto.sendAnonCryptedMessage(theirEndpointDid, message);
};

exports.acceptRequest = async function (theirDid, encryptedMessage) {
  let myDid = await indy.pairwise.getMyDid(theirDid);
  let credentialRequest = await indy.crypto.authDecrypt(myDid, encryptedMessage);
  let [, credDef] = await indy.issuer.getCredDef(await indy.pool.get(), await indy.did.getEndpointDid(), credentialRequest.cred_def_id);

  let credentialOffer;
  let pendingCredOfferId;
  let pendingCredOffers = indy.store.pendingCredentialOffers.getAll();
  for (let pendingCredOffer of pendingCredOffers) {
    if (pendingCredOffer.offer.cred_def_id === credDef.id) {
      pendingCredOfferId = pendingCredOffer.id;
      credentialOffer = pendingCredOffer.offer;
    }
  }
  let schema = await indy.issuer.getSchema(credentialOffer.schema_id);

  let credentialValues = {};
  for (let attr of schema.attrNames) {
    if (credentialOffer.data[attr]) {
      credentialValues[attr] = { raw: credentialOffer.data[attr], encoded: indy.credentials.encode(credentialOffer.data[attr]) };
    }
  }

  console.log(credentialValues);
  const tailsWriterConfig = { 'base_dir': utils.pathToIndyClientHome() + "/tails", 'uri_pattern': '' }
  const blobStorageReaderHandle = await sdk.openBlobStorageReader('default', tailsWriterConfig)

  let [credential, revId, revRegDelta] = await sdk.issuerCreateCredential(await indy.wallet.get(), credentialOffer, credentialRequest, credentialValues, undefined, blobStorageReaderHandle);
  let message = await indy.crypto.buildAuthcryptedMessage(myDid, theirDid, MESSAGE_TYPES.CREDENTIAL, credential);
  let theirEndpointDid = await indy.did.getTheirEndpointDid(theirDid);
  await indy.crypto.sendAnonCryptedMessage(theirEndpointDid, message);
  indy.store.pendingCredentialOffers.delete(pendingCredOfferId);
};

exports.acceptCredential = async function (theirDid, encryptedMessage) {
  let myDid = await indy.pairwise.getMyDid(theirDid);
  let credential = await await indy.crypto.authDecrypt(myDid, encryptedMessage);

  let credentialRequestMetadata;
  let pendingCredentialRequests = indy.store.pendingCredentialRequests.getAll();
  for (let pendingCredReq of pendingCredentialRequests) {
    if (pendingCredReq.credRequestJson.cred_def_id === credential.cred_def_id) { // FIXME: Check for match
      credentialRequestMetadata = pendingCredReq.credRequestMetadataJson;
    }
  }

  let [, credentialDefinition] = await indy.issuer.getCredDef(await indy.pool.get(), await indy.did.getEndpointDid(), credential.cred_def_id);
  await sdk.proverStoreCredential(await indy.wallet.get(), null, credentialRequestMetadata, credential, credentialDefinition);
  let credentials = await indy.credentials.getAll();
  console.log(credentials);
};

exports.encode = function (string) {
  console.log(string);
  if (!string) {
    return string;
  }
  let newString = Buffer.from(string.toString(), 'utf8').toString();
  let number = "1";
  let length = newString.length;
  for (let i = 0; i < length; i++) {
    let codeValue = newString.charCodeAt(i).toString(10);
    if (codeValue.length < 3) {
      codeValue = "0" + codeValue;
    }
    number += codeValue;
  }
  console.log(number);
  return number;
};

exports.decode = function (number) {
  console.log(number);
  if (!number) return number;
  let string = "";
  number = number.slice(1); // remove leading 1
  let length = number.length;

  for (let i = 0; i < length;) {
    let code = number.slice(i, i += 3);
    string += String.fromCharCode(parseInt(code, 10));
  }
  console.log(string);
  return string;
};

async function issueCredentialToSelf(credentialDefinitionId, credentialData) {
  try {
    let endpointDID = await indy.did.getEndpointDid();
    let wallet = await indy.wallet.get();
    let pool = await indy.pool.get();
    let credentialOffer = await sdk.issuerCreateCredentialOffer(wallet, credentialDefinitionId);
    try {
      credentialOffer.data = JSON.parse(credentialData);
    } catch (e) {
      credentialOffer.data = {};
      console.log(e);
    }
    let [, credentialDefinition] = await indy.issuer.getCredDef(pool, endpointDID, credentialOffer.cred_def_id);
    let masterSecretId = await indy.did.getEndpointDidAttribute('master_secret_id');
    let [credRequestJson, credRequestMetadataJson] = await sdk.proverCreateCredentialReq(wallet, endpointDID, credentialOffer, credentialDefinition, masterSecretId);
    let schema = await indy.issuer.getSchema(credentialOffer.schema_id);

    let credentialValues = {};
    for (let attr of schema.attrNames) {
      if (credentialOffer.data[attr]) {
        credentialValues[attr] = { raw: credentialOffer.data[attr], encoded: indy.credentials.encode(credentialOffer.data[attr]) };
      }
    }
    const tailsWriterConfig = { 'base_dir': utils.pathToIndyClientHome() + "/tails", 'uri_pattern': '' }
    const blobStorageReaderHandle = await sdk.openBlobStorageReader('default', tailsWriterConfig)
    let [credential] = await sdk.issuerCreateCredential(wallet, credentialOffer, credRequestJson, credentialValues, undefined, blobStorageReaderHandle);
    await sdk.proverStoreCredential(wallet, null, credRequestMetadataJson, credential, credentialDefinition);
  } catch (e) {
    console.log(e);
  }
}